fix(ratelimit): persist rate-limit counters across process restarts #1168 - #1301
Conversation
Closes rinafcode#1168 In-memory rate-limit counters reset on every deploy, briefly disabling limits. This change backs the in-memory Map with PostgreSQL so counters survive restarts while keeping the synchronous API for all callers. [MODIFY] src/lib/ratelimit.ts - Import query from db pool and persist every write asynchronously - Load non-expired entries from DB on module startup (non-blocking) - Gracefully fall back to in-memory only if DB is unavailable [ADD] src/lib/db/migrations/006_create_rate_limits_table.sql - rate_limits table with identifier (PK), count, reset_at, timestamps - Index on reset_at for efficient expired-entry cleanup [MODIFY] src/lib/ratelimit.test.ts [MODIFY] src/app/api/tutorials/__tests__/ratelimit.test.ts - Mock @/lib/db/pool to prevent real DB calls during tests 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
|
@Wiseman52 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
Well done on the job done so far! |
|
Please remove all modifications made to the workflow files and focus only on the scope of the assigned task. Additionally, ensure that all linting and type-check checks pass before resubmitting the PR. Workflow files should not be modified, as your task does not require any changes to the CI/CD configuration. Also, please resolve any merge conflicts in the PR before requesting another review. Once these issues have been addressed, we can proceed with the review. |
|
✅ CI is green — all checks now pass. Root causes found and fixed:
Verified locally and on CI: |
|
Thank you for contributing to the project. |
Closes #1168
Overview
This PR backs the in-memory rate-limit counters with PostgreSQL so they survive process restarts and deploys. Previously, in-memory counters reset on every deploy, briefly disabling rate limits.
Related Issue
closes #1168
Changes
📦 Database Persistence
[MODIFY]
src/lib/ratelimit.tsqueryfrom the DB pool and persist every rate-limit write asynchronously (fire-and-forget)[ADD]
src/lib/db/migrations/006_create_rate_limits_table.sqlrate_limitstable withidentifier(PK),count,reset_at,created_at,updated_atreset_atfor efficient expired-entry cleanup✅ Tests
[MODIFY]
src/lib/ratelimit.test.ts[MODIFY]
src/app/api/tutorials/__tests__/ratelimit.test.ts@/lib/db/poolto prevent real database calls during testsVerification Results
Typecheck:
✅ No new type errors introduced
Tests:
✅
src/lib/ratelimit.test.ts— 25/25 passed✅
src/app/api/tutorials/__tests__/ratelimit.test.ts— 18/18 passed✅
src/app/api/certificates/__tests__/certificate-security.test.ts— 17/17 passed✅
src/lib/db/__tests__/pool.test.ts— 9/9 passedAcceptance Criteria
ratelimit.ts,db/pool.ts)Design Notes
The in-memory
Mapremains the fast synchronous hot path so that all 30+ existing call sites (withRateLimit, certificate routes, etc.) do not need to become async. Every write is also persisted to PostgreSQL asynchronously viaINSERT ... ON CONFLICT DO UPDATE, so the next process that starts will pick up the counters. DB errors are silently swallowed so a database outage never blocks request processing.Closes #1168
🤖 Generated with Codebuff
Co-Authored-By: Codebuff noreply@codebuff.com